package com.woniuxy.project115.kaer.tenant.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.project115.kaer.commons.util.SMSUtil;
import com.woniuxy.project115.kaer.tenant.config.WebSocketServer;
import com.woniuxy.project115.kaer.tenant.dto.DailySaleDataDto;
import com.woniuxy.project115.kaer.tenant.dto.MonthlySaleDataDto;
import com.woniuxy.project115.kaer.tenant.entity.AppointmentItem;
import com.woniuxy.project115.kaer.tenant.entity.OrderLog;
import com.woniuxy.project115.kaer.tenant.entity.ServiceOrder;
import com.woniuxy.project115.kaer.tenant.entity.TenantStore;
import com.woniuxy.project115.kaer.tenant.mapper.AppointmentItemMapper;
import com.woniuxy.project115.kaer.tenant.mapper.OrderLogMapper;
import com.woniuxy.project115.kaer.tenant.mapper.ServiceOrderMapper;
import com.woniuxy.project115.kaer.tenant.mapper.TenantStoreMapper;
import com.woniuxy.project115.kaer.tenant.param.GetServiceOrderParam;
import com.woniuxy.project115.kaer.tenant.service.ServiceOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.project115.kaer.tenant.util.Data;
import com.woniuxy.project115.kaer.tenant.util.XxlJobUtil;
import com.xxl.job.core.context.XxlJobHelper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author HuH
 * @since 2025年02月18日
 */
@Service
public class ServiceOrderServiceImpl extends ServiceImpl<ServiceOrderMapper, ServiceOrder> implements ServiceOrderService {
    @Resource
    private ServiceOrderMapper serviceOrderMapper;

    @Resource
    private AppointmentItemMapper appointmentItemMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private AlipayClient alipayClient;

    @Resource
    private OrderLogMapper orderLogMapper;

    @Value("${alipay.config.alipay-public-key}")
    private String alipayPublicKey;

    @Value("${alipay.config.alipay-returnUrl}")
    private String alipayReturnUrl;

    @Value("${alipay.config.alipay-notifyUrl}")
    private String alipayNotifyUrl;

    @Resource
    private TenantStoreMapper tenantStoreMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 处理用户支付请求
     *
     * @param serviceOrderNum 订单编号
     * @param userId 用户ID
     * @return 支付表单HTML内容
     * @throws Exception 如果订单不存在或已支付，或者支付请求失败
     */
    @Override
    public String pay(String serviceOrderNum, Integer userId) throws Exception {
        // 判断订单是否存在
        ServiceOrder serviceOrder = serviceOrderMapper.selectById(serviceOrderNum);
        if (serviceOrder == null) {
            throw new Exception("订单不存在");
        }
        if (serviceOrder.getPayState() != Data.PAY_STATE_NOT_PAY) {
            throw new Exception("订单已支付");
        }

        // 更新订单操作日志
        orderLogMapper.insert(
                new OrderLog()
                        .setOperator("用户" + userId)
                        .setServiceOrderNum(serviceOrderNum)
                        .setOperateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
                        .setOperateType("用户发起支付")
        );

        // 拼接支付宝所需参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("out_trade_no", serviceOrderNum);
        params.put("total_amount", serviceOrder.getTotalPrice());
        params.put("subject", "订单支付");
        params.put("body", "支付订单金额给" + serviceOrder.getStoreName());
        params.put("product_code", "FAST_INSTANT_TRADE_PAY");
        // 使用系统默认时区
//        ZoneId zoneId = ZoneId.systemDefault();
//        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(zoneId);
        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
        // 获取时间戳 单位为毫秒
        long timeStamp = zonedDateTime.toInstant().toEpochMilli();
        Date date = new Date(timeStamp + 15 * 60 * 1000);
        String expire = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        System.out.println(expire);
        params.put("time_expire", expire);

        // 实例化网页支付请求对象
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 设置业务内容（值对参数）
        request.setBizContent(JSONUtil.toJsonStr(params));
        // 设置同步回调
        request.setReturnUrl(alipayReturnUrl);
        // 设置异步回调
        request.setNotifyUrl(alipayNotifyUrl);
        // 发送支付请求
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        // 判断支付宝响应结果
        if(response.isSuccess()) {
            // 将订单编号加入到延迟队列 用于在一段时间之后检查订单的支付状态
            CorrelationData messageId = new CorrelationData(UUID.randomUUID().toString());
            rabbitTemplate.convertAndSend("", "delay_check_order_queue", serviceOrderNum, messageId);

            // 返回支付宝响应的支付表单html内容
            return response.getBody();
        }
        throw new Exception("发起支付失败");
    }

    /**
     * 处理支付宝回调通知
     *
     * @param params 支付宝回调参数
     * @throws Exception 如果验签失败或订单状态异常
     */
    @Override
    public void handleOrder(Map<String, String> params) throws Exception {
        // 验签
        boolean b = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "utf-8", "RSA2");
        if (b) {
            // 获取订单编号及支付时间
            String serviceOrderNum = params.get("out_trade_no");
            String tradeStatus = params.get("trade_status");
            System.out.println(params);
            if("TRADE_SUCCESS".equals(tradeStatus)) {
                RLock lock = redissonClient.getFairLock(serviceOrderNum);

                try {
                    lock.lock();
                    ServiceOrder serviceOrder = serviceOrderMapper.selectById(serviceOrderNum);
                    if (serviceOrder.getPayState() == Data.PAY_STATE_PAYED) {//如果状态已经更改为已支付 直接返回
                        return;
                    }
                    String time = params.get("gmt_payment");
                    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                    LocalDateTime payTime = LocalDateTime.parse(time, fmt);
                    // 更新订单的支付状态 支付时间
                    serviceOrder.setPayState(Data.PAY_STATE_PAYED)
                            .setPayTime(payTime);
                    if (serviceOrder.getState() == Data.SERVICE_ORDER_WAIT_PAYMENT) {
                        serviceOrder.setState(Data.SERVICE_ORDER_WAIT_EVALUATE);
                        // 将订单号放入延迟队列中 到期自动好评
                        rabbitTemplate.convertAndSend("", "delay_evaluate_order_queue", serviceOrderNum);
                    }
                    serviceOrderMapper.updateById(serviceOrder);

                    // 更新订单操作日志
                    orderLogMapper.insert(
                            new OrderLog()
                                    .setOperator("用户")
                                    .setServiceOrderNum(serviceOrderNum)
                                    .setOperateTime(serviceOrder.getPayTime())
                                    .setOperateType("用户支付成功")
                    );
                } finally {
                    lock.unlock();
                }

            }
        }
    }

    /**
     * 完成服务订单，设置订单状态为待验收，并通知用户验收
     *
     * @param serviceOrderNum 订单编号
     * @throws Exception 如果订单不存在或服务项目状态异常
     */
    @Override
    public void completeServiceOrder(String serviceOrderNum) throws Exception {
        ServiceOrder serviceOrder = serviceOrderMapper.selectById(serviceOrderNum);
        if (serviceOrder == null) {
            throw new Exception("订单不存在");
        }
        List<AppointmentItem> itemList = appointmentItemMapper.selectList(new QueryWrapper<AppointmentItem>().eq("service_order_num", serviceOrderNum));
        for (AppointmentItem item : itemList) {
            if (item.getState() != Data.APPOINTMENT_ITEM_SERVICED) {
                throw new Exception("还有未完成的服务项目");
            }
        }
        serviceOrder.setState(Data.SERVICE_ORDER_WAIT_CHECK);// 订单状态设置为待验收
        serviceOrderMapper.updateById(serviceOrder);

        //websocket消息推送 通知用户验收
        WebSocketServer.sendInfo("您的订单号为" + serviceOrderNum + "的服务订单已完成，请及时验收。", String.valueOf(serviceOrder.getUserId()));
        // 发送短信通知用户验收
        SMSUtil.sendCode(serviceOrder.getUserPhone(), "[卡尔养车]您的订单号为" + serviceOrderNum + "的服务订单已完成，请及时验收。");
        // 动态创建一个xxljob定时任务，24小时后自动调用自动验收的方法
        LocalDateTime executeTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai")).plusHours(24);
        XxlJobUtil.addXxlJob("自动验收服务订单" + serviceOrderNum, "autoCheckOrderJobHandler", serviceOrderNum, executeTime);

        // 更新商家服务次数
        TenantStore tenantStore = tenantStoreMapper.selectById(serviceOrder.getStoreId());
        tenantStore.setServiceNumber(tenantStore.getServiceNumber() + 1);
        tenantStoreMapper.updateById(tenantStore);

        // 更新订单日志
        orderLogMapper.insert(
                new OrderLog()
                        .setOperator("系统")
                        .setServiceOrderNum(serviceOrderNum)
                        .setOperateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
                        .setOperateType("商家完成服务订单，通知用户验收")
        );
    }

    /**
     * 用户确认验收服务订单
     *
     * @param serviceOrderNum 订单编号
     * @param userId 用户ID
     * @throws Exception 如果订单不存在、状态异常或用户ID不匹配
     */
    @Override
    public void userConfirm(String serviceOrderNum, Integer userId) throws Exception {
        RLock lock = redissonClient.getFairLock(serviceOrderNum);
        try {
            lock.lock();
            ServiceOrder serviceOrder = serviceOrderMapper.selectById(serviceOrderNum);
            if (serviceOrder == null) {
                throw new Exception("订单不存在");
            }
            if (serviceOrder.getState() == Data.SERVICE_ORDER_COMPLETED || serviceOrder.getState() == Data.SERVICE_ORDER_WAIT_EVALUATE || serviceOrder.getState() == Data.SERVICE_ORDER_WAIT_PAYMENT) {
                throw new Exception("订单已验收，请勿重复操作");
            }
            if (serviceOrder.getState() != Data.SERVICE_ORDER_WAIT_CHECK) {
                throw new Exception("服务尚未完成，无法验收");
            }
            if (serviceOrder.getUserId() != userId) {
                throw new Exception("订单不属于该用户");
            }

            if (serviceOrder.getPayState() == Data.PAY_STATE_PAYED) {// 用户已经支付过了
                serviceOrder.setState(Data.SERVICE_ORDER_WAIT_EVALUATE);
                serviceOrderMapper.updateById(serviceOrder);

            } else {// 用户还未支付
                serviceOrder.setState(Data.SERVICE_ORDER_WAIT_PAYMENT);
                serviceOrderMapper.updateById(serviceOrder);
            }

            // 更新订单日志
            orderLogMapper.insert(
                    new OrderLog()
                            .setOperator("用户")
                            .setServiceOrderNum(serviceOrderNum)
                            .setOperateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
                            .setOperateType("用户确认验收")
            );
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Page<ServiceOrder> getServiceOrder(GetServiceOrderParam param, Integer storeId) throws Exception {
        Page<ServiceOrder> page = new Page<>(param.getPageNum(), param.getPageSize());
        QueryWrapper<ServiceOrder> queryWrapper = new QueryWrapper<>();
        if (param.getKeyword() != null && !param.getKeyword().isEmpty()) {//关键字搜索
            queryWrapper.like("service_order_num", param.getKeyword())
                    .or()
                    .like("user_name", param.getKeyword())
                    .or()
                    .like("vehicle_num", param.getKeyword())
                    .or()
                    .like("user_phone", param.getKeyword());
        }
        if (param.getTypeName() != null && !param.getTypeName().isEmpty()) {
            queryWrapper.eq("type_name", param.getTypeName());
        }
        if (param.getState() != null && param.getState() != -1) {
            queryWrapper.eq("state", param.getState());
        }
        if (param.getPayState() != null && param.getPayState() != -1) {
            queryWrapper.eq("pay_state", param.getPayState());
        }
        queryWrapper.eq("store_id", storeId);
        return serviceOrderMapper.selectPage(page, queryWrapper);
    }

    @Override
    public DailySaleDataDto getDailySalesData(LocalDate date, Integer storeId) {
        if (LocalDate.now().equals(date)) {//如果查询的是当天的数据，实时查询
            //获取当天0点-24点之内的订单
            LocalDateTime startTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
            List<ServiceOrder> list = serviceOrderMapper.selectList(
                    new QueryWrapper<ServiceOrder>()
                            .between("create_time", startTime, endTime)
                            .eq("store_id", storeId));

            // 统计销售数据
            DailySaleDataDto dailySaleDataDto = new DailySaleDataDto();
            if (list.isEmpty()) { //如果当天没有订单
                dailySaleDataDto.setTotalSales(0.0);
                dailySaleDataDto.setTotalOrders(0);
            } else {
                dailySaleDataDto.setTotalSales(list.stream().mapToDouble(ServiceOrder::getTotalPrice).sum());
                dailySaleDataDto.setTotalOrders(list.size());
            }
            dailySaleDataDto.setStoreId(storeId);
            dailySaleDataDto.setDate(date);
            return dailySaleDataDto;
        } else {//如果是之前的数据则查询redis
            String dateKey = "sales:data:" + storeId + "date" + date;
            String saleDataJson = stringRedisTemplate.opsForValue().get(dateKey);
            return JSONUtil.toBean(saleDataJson, DailySaleDataDto.class);
        }

    }

    @Override
    public MonthlySaleDataDto getMonthlySalesData(LocalDate date, Integer storeId) {
        //根据date获取到当前的月份
        Integer monthValue = date.minusDays(1).getMonthValue();
        String monthKey = "sales:data:" + storeId + "month" + monthValue;

        //获取本月内，今天之前的销售数据+今天的实时销售数据
        MonthlySaleDataDto monthlyDto = new MonthlySaleDataDto();
        if (stringRedisTemplate.hasKey(monthKey)) {
            monthlyDto = JSONUtil.toBean(stringRedisTemplate.opsForValue().get(monthKey), MonthlySaleDataDto.class);
            DailySaleDataDto dailySalesData = getDailySalesData(date, storeId);
            monthlyDto.setTotalSales(monthlyDto.getTotalSales() + dailySalesData.getTotalSales());
            monthlyDto.setTotalOrders(monthlyDto.getTotalOrders() + dailySalesData.getTotalOrders());
        } else {
            monthlyDto = BeanUtil.toBean(getDailySalesData(date, storeId), MonthlySaleDataDto.class);
        }

        return monthlyDto;
    }

    //每天0时，定时更新前一天 以及 当月的销售数据
    public void generateDailySalesData() throws Exception {
        List<TenantStore> tenantStores = tenantStoreMapper.selectAll();
        List<Integer> storeIds = tenantStores.stream().map(TenantStore::getStoreId).collect(Collectors.toList());

        for (Integer storeId : storeIds) {
            RLock lock = redissonClient.getFairLock(storeId + "");
            try {
                lock.lock();
                QueryWrapper<ServiceOrder> queryWrapper = new QueryWrapper<>();
                // 获取下单时间在前一天内的订单
                LocalDateTime todayStart = LocalDate.now(ZoneId.of("Asia/Shanghai")).atStartOfDay();
                LocalDateTime yesterdayStart = todayStart.minusDays(1);

                queryWrapper.lambda()
                        .eq(ServiceOrder::getStoreId, storeId)
                        .ge(ServiceOrder::getCreateTime, yesterdayStart)
                        .lt(ServiceOrder::getCreateTime, todayStart); // 使用左闭右开区间

                List<ServiceOrder> list = serviceOrderMapper.selectList(queryWrapper);

                // 统计销售数据
                DailySaleDataDto dailySaleDataDto = new DailySaleDataDto();
                if (list.isEmpty()) { //如果当天没有订单
                    dailySaleDataDto.setTotalSales(0.0);
                    dailySaleDataDto.setTotalOrders(0);
                } else {
                    dailySaleDataDto.setTotalSales(list.stream().mapToDouble(ServiceOrder::getTotalPrice).sum());
                    dailySaleDataDto.setTotalOrders(list.size());
                }
                dailySaleDataDto.setStoreId(storeId);
                dailySaleDataDto.setDate(LocalDate.now(ZoneId.of("Asia/Shanghai")).minusDays(1));

                // 获取当前日期
                String dateKey = "sales:data:" + storeId + "date" + LocalDate.now().minusDays(1);

                // 将销售数据存入 Redis
                String saleDataJson = JSONUtil.toJsonStr(dailySaleDataDto);
                stringRedisTemplate.opsForValue().set(dateKey, saleDataJson, 31, TimeUnit.DAYS);

                //更新当月数据
                String monthKey = "sales:data:" + storeId + "month" + LocalDate.now().minusDays(1).getMonthValue();
                if (stringRedisTemplate.hasKey(monthKey)) {//如果每月销售数据这个key存在，则更新
                    MonthlySaleDataDto monthlyDto = JSONUtil.toBean(stringRedisTemplate.opsForValue().get(monthKey), MonthlySaleDataDto.class);
                    monthlyDto.setTotalSales(monthlyDto.getTotalSales() + dailySaleDataDto.getTotalSales());
                    monthlyDto.setTotalOrders(monthlyDto.getTotalOrders() + dailySaleDataDto.getTotalOrders());
                    stringRedisTemplate.opsForValue().set(monthKey, JSONUtil.toJsonStr(monthlyDto), 31, TimeUnit.DAYS);
                } else {
                    MonthlySaleDataDto monthlySaleDataDto = BeanUtil.toBean(dailySaleDataDto, MonthlySaleDataDto.class);
                    monthlySaleDataDto.setMonthValue(LocalDate.now().minusDays(1).getMonthValue());
                    stringRedisTemplate.opsForValue().set(monthKey, JSONUtil.toJsonStr(monthlySaleDataDto), 31, TimeUnit.DAYS);
                }

            } catch (Exception e) {
                XxlJobHelper.log("统计销售数据失败: " + e.getMessage());
                throw e;
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    public List<DailySaleDataDto> getSalesDataList(Integer number, Integer storeId) throws Exception {
        //获取当前时间
        LocalDate now = LocalDate.now();
        //根据number来获取前number天的销售数据
        List<DailySaleDataDto> salesDataList = new ArrayList<>();
        for (int i = 1; i <= number; i++) {
            LocalDate date = now.minusDays(i);
            String dateKey = "sales:data:" + storeId + "date" + date;
            if (stringRedisTemplate.hasKey(dateKey)) {
                String saleDataJson = stringRedisTemplate.opsForValue().get(dateKey);
                DailySaleDataDto dailySaleDataDto = JSONUtil.toBean(saleDataJson, DailySaleDataDto.class);
                salesDataList.add(dailySaleDataDto);
            }
        }
        System.out.println("salesDataList = " + salesDataList);
        return salesDataList;
    }
}
